/*
 * jQuery MultiSelect UI Widget 1.9
 * Copyright (c) 2011 Eric Hynds
 *
 * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
 *
 * Depends:
 *   - jQuery 1.4.2+
 *   - jQuery UI 1.8 widget factory
 *
 * Optional:
 *   - jQuery UI effects
 *   - jQuery UI position utility
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
*/
/*
 * yyjob: update
 * disable multiselect_id if it's not needed 
 */
(function($, undefined){

var multiselectID = 0;

$.widget("ech.multiselect", {
	
	// default options
	options: {
		header: true,
		height: 175,
		minWidth: 225,
		classes: '',
		checkAllText: 'Check all',
		uncheckAllText: 'Uncheck all',
		noneSelectedText: 'Select options',
		selectedText: '# selected',
		selectedList: 0,
		show: 'fade',
		hide: 'fade',
		autoOpen: false,
		multiple: true,
		position: {},
      name: true
	},

	_create: function(){
		var el = this.element.hide(),
			o = this.options;
		
		this.speed = $.fx.speeds._default; // default speed for effects
		this._isOpen = false; // assume no
	
		var 
			button = (this.button = $('<button type="button"><span class="ui-icon ui-icon-triangle-2-n-s"></span></button>'))
				.addClass('ui-multiselect ui-widget ui-state-default ui-corner-all')
				.addClass( o.classes )
				.attr({ 'title':el.attr('title'), 'aria-haspopup':true })
				.insertAfter( el ),
			
			buttonlabel = (this.buttonlabel = $('<span />'))
				.html( o.noneSelectedText )
				.appendTo( button ),
				
			menu = (this.menu = $('<div />'))
				.addClass('ui-multiselect-menu ui-widget ui-widget-content ui-corner-all')
				.addClass( o.classes )
            .zIndex( this.element.zIndex() + 1 )
				.insertAfter( button ),
				
			header = (this.header = $('<div />'))
				.addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix')
				.appendTo( menu ),
				
			headerLinkContainer = (this.headerLinkContainer = $('<ul />'))
				.addClass('ui-helper-reset')
				.html(function(){
					if( o.header === true ){
						return '<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>' + o.checkAllText + '</span></a></li><li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>' + o.uncheckAllText + '</span></a></li>';
					} else if(typeof o.header === "string"){
						return '<li>' + o.header + '</li>';
					} else {
						return '';
					}
				})
				.append('<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon ui-icon-circle-close"></span></a></li>')
				.appendTo( header ),
			
			checkboxContainer = (this.checkboxContainer = $('<ul />'))
				.addClass('ui-multiselect-checkboxes ui-helper-reset')
				.appendTo( menu );

		// perform event bindings
		this._bindEvents();
		
		// build menu
		this.refresh( true );
		
		// some addl. logic for single selects
		if( !o.multiple ){
			menu.addClass('ui-multiselect-single');
		}
	},
	
	_init: function(){
		if( this.options.header === false ){
			this.header.hide();
		}
		if( !this.options.multiple ){
			this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide();
		}
		if( this.options.autoOpen ){
			this.open();
		}
		if( this.element.is(':disabled') ){
			this.disable();
		}
	},
	
	refresh: function( init ){
		var el = this.element,
			o = this.options,
			menu = this.menu,
			button = this.button,
			checkboxContainer = this.checkboxContainer,
			optgroups = [],
			id = el.attr('id') || multiselectID++; // unique ID for the label & option tags
		
		checkboxContainer.empty();
		
      // yyjob: check name boolean 
      var setName = this.options.name; 

		// build items
		this.element.find('option').each(function(i){
			var $this = $(this), 
				title = $this.html(),
				value = this.value, 
				inputID = this.id || 'ui-multiselect-'+id+'-option-'+i, 
				$parent = $this.parent(), 
				isDisabled = $this.is(':disabled'), 
				labelClasses = ['ui-corner-all'],
				label, li;
			
			// is this an optgroup?
			if( $parent.is('optgroup') ){
				var optLabel = $parent.attr('label');
				
				// has this optgroup been added already?
				if( $.inArray(optLabel, optgroups) === -1 ){
					$('<li><a href="#">' + optLabel + '</a></li>')
						.addClass('ui-multiselect-optgroup-label')
						.appendTo( checkboxContainer );
					
					optgroups.push( optLabel );
				}
			}
			
			if( value.length > 0 ){
				if( isDisabled ){
					labelClasses.push('ui-state-disabled');
				}
				
				li = $('<li />')
					.addClass(isDisabled ? 'ui-multiselect-disabled' : '')
					.appendTo( checkboxContainer );
				
            //yyjob: add active class if it's true 	
				label = $('<label />')
					.attr('for', inputID)
					.addClass(labelClasses.join(' '))
               .addClass(this.selected ? "ui-state-active" : "")
					.appendTo( li );
				
				// attr's are inlined to support form reset.  double checked attr is to support chrome bug - see #46
				// yyjob: check whether name is needed 
				$('<input type="'+(o.multiple ? 'checkbox' : 'radio')+'" '+(this.selected ? 'checked="checked"' : '')+ (setName ? ' name="multiselect_'+id + '"' : '') + '/>')
					.prop({ checked:this.selected, disabled:isDisabled, 'aria-disabled':isDisabled, 'aria-selected':this.selected })
					.attr({ id:inputID, title:title })
					.val( value )
					.appendTo( label )
					.after('<span>'+title+'</span>');
			}
		});
		
		// cache some moar useful elements
		this.labels = menu.find('label');
		
		// set widths
		this._setButtonWidth();
		this._setMenuWidth();
		
		// remember default value
		button[0].defaultValue = this.update();
		
		// broadcast refresh event; useful for widgets
		if( !init ){
			this._trigger('refresh');
		}
	},
	
	// updates the button text.  call refresh() to rebuild
	update: function(){
		var o = this.options,
			$inputs = this.labels.find('input'),
			$checked = $inputs.filter(':checked'),
			numChecked = $checked.length,
			value;

		if( numChecked === 0 ){
			value = o.noneSelectedText;
		} else {
			if($.isFunction(o.selectedText)){
				value = o.selectedText.call(this, numChecked, $inputs.length, $checked.get());
			} else if( /\d/.test(o.selectedList) && o.selectedList > 0 && numChecked <= o.selectedList){
				value = $checked.map(function(){ return this.title; }).get().join(', ');
			} else {
				value = o.selectedText.replace('#', numChecked).replace('#', $inputs.length);
			}
		}

		this.buttonlabel.html( value );
		return value;
	},
	
	// binds events
	_bindEvents: function(){
		var self = this, button = this.button;
		
		function clickHandler(){
			self[ self._isOpen ? 'close' : 'open' ]();
			return false;
		}
		
		// webkit doesn't like it when you click on the span :(
		button
			.find('span')
			.bind('click.multiselect', clickHandler);
		
		// button events
		button.bind({
			click: clickHandler,
			keypress: function(e){
				switch(e.which){
					case 27: // esc
					case 38: // up
					case 37: // left
						self.close();
						break;
					case 39: // right
					case 40: // down
						self.open();
						break;
				}
			},
			mouseenter: function(){
				if( !button.hasClass('ui-state-disabled') ){
					$(this).addClass('ui-state-hover');
				}
			},
			mouseleave: function(){
				$(this).removeClass('ui-state-hover');
			},
			focus: function(){
				if( !button.hasClass('ui-state-disabled') ){
					$(this).addClass('ui-state-focus');
				}
			},
			blur: function(){
				$(this).removeClass('ui-state-focus');
			}
		});

		// header links
		this.header
			.delegate('a', 'click.multiselect', function(e){
				// close link
				if( $(this).hasClass('ui-multiselect-close') ){
					self.close();
			
				// check all / uncheck all
				} else {
					self[ $(this).hasClass('ui-multiselect-all') ? 'checkAll' : 'uncheckAll' ]();
				}
			
				e.preventDefault();
			});
		
		// optgroup label toggle support
		this.menu
			.delegate('li.ui-multiselect-optgroup-label a', 'click.multiselect', function(e){
				e.preventDefault();
				
				var $this = $(this),
					$inputs = $this.parent().nextUntil('li.ui-multiselect-optgroup-label').find('input:visible:not(:disabled)');
				
				// trigger event and bail if the return is false
				if( self._trigger('optgrouptoggle', e, { inputs:$inputs.get(), label:$this.parent().text(), checked:$inputs[0].checked }) === false ){
					return;
				}
				
				// toggle inputs
				self._toggleChecked(
					$inputs.filter(':checked').length !== $inputs.length,
					$inputs
				);
			})
			.delegate('label', 'mouseenter.multiselect', function(){
				if( !$(this).hasClass('ui-state-disabled') ){
					self.labels.removeClass('ui-state-hover');
					$(this).addClass('ui-state-hover').find('input').focus();
				}
			})
			.delegate('label', 'keydown.multiselect', function(e){
				switch(e.which){
					case 9: // tab
					case 27: // esc
						self.close();
						break;
					case 38: // up
					case 40: // down
					case 37: // left
					case 39: // right
						self._traverse(e.which, this);
						e.preventDefault();
						break;
					case 13: // enter
						e.preventDefault();
						$(this).find('input')[0].click();
						break;
				}
			})
			.delegate('input[type="checkbox"], input[type="radio"]', 'click.multiselect', function(e){
				var $this = $(this),
					val = this.value,
					checked = this.checked,
					tags = self.element.find('option');
				
				// bail if this input is disabled or the event is cancelled
				if( $this.is(':disabled') || self._trigger('click', e, { value:val, text:this.title, checked:checked }) === false ){
					e.preventDefault();
					return;
				}
				
				// toggle aria state
				$this.prop('aria-selected', checked);

				// set the original option tag to selected
				tags.filter(function(){
					return this.value === val;
				}).prop('selected', (checked ? true:false));
				
				// make sure the original option tags are unselected first 
				// in a single select
				if( !self.options.multiple ){
					tags.not(function(){
						return this.value === val;
					}).prop('selected', false);
					
					self.labels.removeClass('ui-state-active');
					$this.closest('label').toggleClass('ui-state-active', checked );
					
					// close menu
					self.close();
				}
            // yyjob: we have added ui-state-active on label if it's selected by default. enable it too here 
				else  {
            	$this.closest('label').toggleClass('ui-state-active', checked );
				}

				// setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827
				// http://bugs.jquery.com/ticket/3827 
				setTimeout($.proxy(self.update, self), 10);
			});
		
		// close each widget when clicking on any other element/anywhere else on the page
		$(document).bind('mousedown.multiselect', function(e){
			if(self._isOpen && !$.contains(self.menu[0], e.target) && e.target !== self.button[0]){
				self.close();
			}
		});
		
		// deal with form resets.  the problem here is that buttons aren't
		// restored to their defaultValue prop on form reset, and the reset
		// handler fires before the form is actually reset.  delaying it a bit
		// gives the form inputs time to clear.
		$(this.element[0].form).bind('reset.multiselect', function(){
			setTimeout(function(){ self.update(); }, 10);
		});
	},

	// set button width
	_setButtonWidth: function(){
		var width = this.element.outerWidth(),
			o = this.options,
		   m = this.menu;
			
		if( /\d/.test(o.minWidth) && width < o.minWidth){
			width = o.minWidth;
		}
		
      var v = 	parseInt(m.css('padding-left'),10) + 
				parseInt(m.css('padding-right'),10) +
				parseInt(m.css('border-right-width'),10) +
				parseInt(m.css('border-left-width'),10);

		//hulahoo: set widths, use min-width to be consistent with multiple flat widget 
		this.button.css("min-width", width );
		this.button.css("max-width", this.options.menuWidth ? (this.options.menuWidth+v) : width); 
		this.button.css("width", width );
	},
	
	// set menu width
	_setMenuWidth: function(){
		var m = this.menu,
			width = this.button.outerWidth()-
				parseInt(m.css('padding-left'),10)-
				parseInt(m.css('padding-right'),10)-
				parseInt(m.css('border-right-width'),10)-
				parseInt(m.css('border-left-width'),10);
				
		m.width( width || this.button.outerWidth() );
	},
	
	// move up or down within the menu
	_traverse: function(which, start){
		var $start = $(start),
			moveToLast = which === 38 || which === 37,
			
			// select the first li that isn't an optgroup label / disabled
			$next = $start.parent()[moveToLast ? 'prevAll' : 'nextAll']('li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)')[ moveToLast ? 'last' : 'first']();
		
		// if at the first/last element
		if( !$next.length ){
			var $container = this.menu.find('ul:last');
			
			// move to the first/last
			this.menu.find('label')[ moveToLast ? 'last' : 'first' ]().trigger('mouseover');
			
			// set scroll position
			$container.scrollTop( moveToLast ? $container.height() : 0 );
			
		} else {
			$next.find('label').trigger('mouseover');
		}
	},

	_toggleChecked: function(flag, group){
		var $inputs = (group && group.length) ?
			group :
			this.labels.find('input');

		// toggle state on inputs
		$inputs
			.not(':disabled')
			.prop({ 'checked':flag, 'aria-selected':flag }); 
		
		this.update();
		
		var values = $inputs.map(function(){
			return this.value;
		}).get();
		
		// toggle state on original option tags
		this.element.find('option').filter(function(){
			return !this.disabled && $.inArray(this.value, values) > -1;
		}).prop({ 'selected':flag, 'aria-selected':flag });
	},

	_toggleDisabled: function( flag ){
		this.button
			.prop({ 'disabled':flag, 'aria-disabled':flag })[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled');
		
		this.menu
			.find('input')
			.prop({ 'disabled':flag, 'aria-disabled':flag })
			.parent()[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled');
		
		this.element
			.prop({ 'disabled':flag, 'aria-disabled':flag });
	},
	
	// open the menu
	open: function(e){
		var self = this,
			button = this.button,
			menu = this.menu,
			speed = this.speed,
			o = this.options;
		
		// bail if the multiselectopen event returns false, this widget is disabled, or is already open 
		if( this._trigger('beforeopen') === false || button.hasClass('ui-state-disabled') || this._isOpen ){
			return;
		}
		
		var $container = menu.find('ul:last'),
			effect = o.show,
			pos = button.position();
		
		// figure out opening effects/speeds
		if( $.isArray(o.show) ){
			effect = o.show[0];
			speed = o.show[1] || self.speed;
		}
		
		// set the scroll of the checkbox container
		$container.scrollTop(0).height(o.height);
		
		// position and show menu
		if( $.ui.position && !$.isEmptyObject(o.position) ){
			o.position.of = o.position.of || button;
			
			menu
				.show()
				.position( o.position )
				.hide()
				.show( effect, speed );
		
		// if position utility is not available...
		} else {
			menu.css({ 
				top: pos.top+button.outerHeight(),
				left: pos.left
			}).show( effect, speed );
		}
		
		// select the first option
		// triggering both mouseover and mouseover because 1.4.2+ has a bug where triggering mouseover
		// will actually trigger mouseenter.  the mouseenter trigger is there for when it's eventually fixed
		this.labels.eq(0).trigger('mouseover').trigger('mouseenter').find('input').trigger('focus');
		
		button.addClass('ui-state-active');
		this._isOpen = true;
		this._trigger('open');
	},
	
	// close the menu
	close: function(){
		if(this._trigger('beforeclose') === false){
			return;
		}
	
		var o = this.options, effect = o.hide, speed = this.speed;
		
		// figure out opening effects/speeds
		if( $.isArray(o.hide) ){
			effect = o.hide[0];
			speed = o.hide[1] || this.speed;
		}
	
		this.menu.hide(effect, speed);
		this.button.removeClass('ui-state-active').trigger('blur').trigger('mouseleave');
		this._trigger('close');
		this._isOpen = false;
	},

	enable: function(){
		this._toggleDisabled(false);
	},
	
	disable: function(){
		this._toggleDisabled(true);
	},
	
	checkAll: function(e){
		this._toggleChecked(true);
		this._trigger('checkAll');
	},
	
	uncheckAll: function(){
		this._toggleChecked(false);
		this._trigger('uncheckAll');
	},
	
	getChecked: function(){
		return this.menu.find('input').filter(':checked');
	},
	
	destroy: function(){
		// remove classes + data
		$.Widget.prototype.destroy.call( this );
		
		this.button.remove();
		this.menu.remove();
		this.element.show();
		
		return this;
	},
	
	isOpen: function(){
		return this._isOpen;
	},
	
	widget: function(){
		return this.menu;
	},
	
   //hulahoo: add button call to return button element 
   getButton: function() {
      return this.button;
   },

	// react to option changes after initialization
	_setOption: function( key, value ){
		var menu = this.menu;
		
		switch(key){
			case 'header':
				menu.find('div.ui-multiselect-header')[ value ? 'show' : 'hide' ]();
				break;
			case 'checkAllText':
				menu.find('a.ui-multiselect-all span').eq(-1).text(value);
				break;
			case 'uncheckAllText':
				menu.find('a.ui-multiselect-none span').eq(-1).text(value);
				break;
			case 'height':
				menu.find('ul:last').height( parseInt(value,10) );
				break;
			case 'minWidth':
				this.options[ key ] = parseInt(value,10);
				this._setButtonWidth();
				this._setMenuWidth();
				break;
			case 'selectedText':
			case 'selectedList':
			case 'noneSelectedText':
				this.options[key] = value; // these all needs to update immediately for the update() call
				this.update();
				break;
			case 'classes':
				menu.add(this.button).removeClass(this.options.classes).addClass(value);
				break;
		}
		
		$.Widget.prototype._setOption.apply( this, arguments );
	}
});

})(jQuery);

/*
 * jQuery MultiSelect UI Widget Filtering Plugin 1.1
 * Copyright (c) 2011 Eric Hynds
 *
 * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
 *
 * Depends:
 *   - jQuery UI MultiSelect widget
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
*/
(function($){
   var rEscape = /[\-\[\]{}()*+?.,\\^$|#\s]/g;
   
   $.widget("ech.multiselectfilter", {
      
      options: {
         label: "Filter:",
         width: null, /* override default width set in css file (px). null will inherit */
         placeholder: "Enter keywords"
      },
      
      _create: function(){
         var self = this,
            opts = this.options,
            instance = (this.instance = $(this.element).data("multiselect")),
            
            // store header; add filter class so the close/check all/uncheck all links can be positioned correctly
            header = (this.header = instance.menu.find(".ui-multiselect-header").addClass("ui-multiselect-hasfilter")),
            
            // wrapper elem
            wrapper = (this.wrapper = $('<div class="ui-multiselect-filter">'+(opts.label.length ? opts.label : '')+'<input placeholder="'+opts.placeholder+'" type="search"' + (/\d/.test(opts.width) ? 'style="width:'+opts.width+'px"' : '') + ' /></div>').prependTo( this.header ));

         // reference to the actual inputs
         this.inputs = instance.menu.find('input[type="checkbox"], input[type="radio"]');
         
         // build the input box
         this.input = wrapper
         .find("input")
         .bind({
            keydown: function( e ){
               // prevent the enter key from submitting the form / closing the widget
               if( e.which === 13 ){
                  e.preventDefault();
               }
            },
            keyup: $.proxy(self._handler, self),
            click: $.proxy(self._handler, self)
         });
         
         // cache input values for searching
         this.updateCache();
         
         // rewrite internal _toggleChecked fn so that when checkAll/uncheckAll is fired,
         // only the currently filtered elements are checked
         instance._toggleChecked = function(flag, group){
            var $inputs = (group && group.length) ?
                  group :
                  this.labels.find('input'),
               
               // do not include hidden elems if the menu isn't open.
               selector = self.instance._isOpen ?
                  ":disabled, :hidden" :
                  ":disabled";
            
            // toggle checked
            $inputs.not( selector ).prop('checked', flag); 
            
            // update text
            this.update();
            
            // figure out which option tags need to be selected
            var values = $inputs.map(function(){
               return this.value;
            }).get();
            
            // select option tags
            this.element.find('option').filter(function(){
               return !this.disabled && $.inArray(this.value, values) > -1;
            }).prop({ 'selected':flag, 'aria-selected':flag });
         };
         
         // rebuild cache when multiselect is updated
         $(document).bind("multiselectrefresh", function(){
            self.updateCache();
            self._handler();
         });
      },
      
      // thx for the logic here ben alman
      _handler: function( e ){
         var term = $.trim( this.input[0].value.toLowerCase() ),
         
            // speed up lookups
            rows = this.rows, inputs = this.inputs, cache = this.cache;
         
         if( !term ){
            rows.show();
         } else {
            rows.hide();
            
            var regex = new RegExp(term.replace(rEscape, "\\$&"), 'gi');
            
            this._trigger( "filter", e, $.map(cache, function(v,i){
               if( v.search(regex) !== -1 ){
                  rows.eq(i).show();
                  return inputs.get(i);
               }
               
               return null;
            }));
         }

         // show/hide optgroups
         this.instance.menu.find(".ui-multiselect-optgroup-label").each(function(){
            var $this = $(this);
            $this[ $this.nextUntil('.ui-multiselect-optgroup-label').filter(':visible').length ? 'show' : 'hide' ]();
         });
      },
      
      updateCache: function(){
         // each list item
         this.rows = this.instance.menu.find(".ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup-label)");
         
         // cache
         this.cache = this.element.children().map(function(){
            var self = $(this);
            
            // account for optgroups
            if( this.tagName.toLowerCase() === "optgroup" ){
               self = self.children();
            }
            
            // see _create() in jquery.multiselect.js
            if( !self.val().length ){
               return null;
            }
            
            return self.map(function(){
               return this.innerHTML.toLowerCase();
            }).get();
         }).get();
      },
      
      widget: function(){
         return this.wrapper;
      },
      
      destroy: function(){
         $.Widget.prototype.destroy.call( this );
         this.input.val('').trigger("keyup");
         this.wrapper.remove();
      }
   });
})(jQuery);

/*yyjob: extend ech.multiselectfilter so it can have a few select in one row or click to ajax 
 * we use table instead of ul for display  
 */

(function($){
   $.widget("job.multiflatselect", $.ech.multiselect, {
      
   options: {
         label: "Filter:",
         colNum: 3,
         dataSection: false, 
         dataSelected: [], 
         dataColNum: 3,
         dataContainerHeight: null, 
         dataHeader: true, 
         dataCheckAllText: "Check all", 
         dataUncheckAllText: "Uncheck all", 
         listStyle: false, /* whether to use <ul><li></li></ul>; use table if it's false */
         requestUrl: null, /* a click on the list will trigger another data request; only valid when listStyle is false */
         requestDataType: "json",
         requestData: {}, 
         width: null, /* override default width set in css file (px). null will inherit */
         menuWidth: null,
         dataMultiple: true,
         showDataSectionText: null
   },
	_create: function(){
		var el = this.element.hide(),
			o = this.options;
		
		this.speed = $.fx.speeds._default; // default speed for effects
		this._isOpen = false; // assume no
	
		var 
			button = (this.button = $('<button type="button"><span class="ui-icon ui-icon-triangle-2-n-s"></span></button>'))
				.addClass('ui-multiselect ui-widget ui-state-default ui-corner-all')
				.addClass( o.classes )
				.attr({ 'title':el.attr('title'), 'aria-haspopup':true })
            .css("visibility", o.dataSection ? "hidden" : '')
            .css("height", o.dataSection ? "1" : '')
				.insertAfter( el ),
			
			buttonlabel = (this.buttonlabel = $('<span />'))
				.html( o.noneSelectedText )
				.appendTo( button ),
				
         showdatalabel = (this.showdatalabel = 
                           (o.dataSection && !o.autoOpen && o.showDataSectionText != null && el.parent().find(".ui-multiselect-showdata").length <= 0)? 
                           $('<div style="text-align:right"><a class="ui-multiselect-showdata" href="javascript:;">' + 
                             o.showDataSectionText + '</a></div>').insertAfter(button) : 
                             (el.parent().find(".ui-multiselect-showdata").length> 0 ? el.parent().find(".ui-multiselect-showdata") : button)),

			menu = (this.menu = $('<div />'))
				.addClass('ui-multiselect-menu ui-widget ' + (!o.dataSection ? ' ui-widget-content ui-corner-all' : 'ui-multiselect-datasection-menu'))
				.addClass( o.classes )
            .zIndex( this.element.zIndex() + 1 )
				.insertAfter( showdatalabel ),
				
			header = (this.header = $('<div />'))
				.addClass((!o.dataSection ? ' ui-widget-header ui-corner-all ' : 'ui-multiselect-dataheader ') + ' ui-multiselect-header ui-helper-clearfix')
				.appendTo( menu ),
				
			headerLinkContainer = (this.headerLinkContainer = $('<ul />'))
				.addClass('ui-helper-reset' + (o.dataSection ? ' ui-helper-dataclose' : ''))
				.html(function(){
					if( o.header === true ){
						return '<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>' + o.checkAllText + '</span></a></li><li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>' + o.uncheckAllText + '</span></a></li>';
					} else if(typeof o.header === "string"){
						return '<li>' + o.header + '</li>';
					} else {
						return '';
					}
				})
				.append(o.dataSection ? '' : '<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon ui-icon-circle-close"></span></a></li>')
				.appendTo( header ),
			
			checkboxContainerDiv = (this.checkboxContainerDiv = $('<div />'))
				.addClass((o.dataSection ? 'ui-multiselect-datasection' : 'ui-multiselect-flatboxes') + ' ui-widget')
				.addClass( o.classes )
            .css("height",  o.dataContainerHeight ? o.dataContainerHeight : '')
				.appendTo( menu ),

			checkboxContainer = (this.checkboxContainer = $('<table />'))
				.addClass('ui-multiselect-table ui-helper-reset')
				.appendTo( checkboxContainerDiv );
		
		// perform event bindings
		this._bindEvents();
		
      this._bindTableEvents(); 
		// build menu
		this.refresh( true );
		
		// some addl. logic for single selects
		if( !o.multiple ){
			menu.addClass('ui-multiselect-single');
		}

		if(o.dataSection && !this.element.find('option').prop('selected')) {
         this.checkAll();
      }
	},

	refresh: function( init ){
		var el = this.element,
			o = this.options,
			menu = this.menu,
			button = this.button,
			checkboxContainer = this.checkboxContainer,
			optgroups = [],
			id = el.attr('id') || multiselectID++; // unique ID for the label & option tags
		
		checkboxContainer.empty();
		
      // yyjob: check name boolean 
      var setName = this.options.name; 

      var colNum = this.options.colNum; 
      var listStyle = this.options.listStyle; 
      var num = 0; 
      var colEl = null; 
      var requestUrl = this.options.requestUrl; 

      /* parse out the stored data list */
      var valueslist = this.options.dataSelected; //flatMultiSelectData(this.options.dataSelected); 
      var allselectlist = (valueslist && valueslist.allselect) ? valueslist.allselect : []; 
      valueslist = (valueslist && valueslist.valueslist) ? valueslist.valueslist : []; 

		// build items
		this.element.find('option').each(function(i){
			var $this = $(this), 
				title = $this.html(),
				value = this.value, 
				inputID = this.id || 'ui-multiselect-'+id+'-option-'+i, 
				$parent = $this.parent(), 
				isDisabled = $this.is(':disabled'), 
				labelClasses = ['ui-corner-all'],
				label, li;
			
			// is this an optgroup? ignore since we don't support it 
			
			if( value.length > 0 ){
				if( isDisabled ){
					labelClasses.push('ui-state-disabled');
				}
            if(num % colNum == 0) {
               colEl = $('<tr />').appendTo(checkboxContainer); 
            }

            num++; 
 
            if(listStyle) {  
				   li = $('<td />')
					   .addClass(isDisabled ? 'ui-multiselect-disabled' : '')
					   .appendTo(colEl); 
            	
				   label = $('<label />')
					   .attr('for', inputID)
					   .addClass(labelClasses.join(' '))
					   .appendTo( li );
				
			   	// attr's are inlined to support form reset.  double checked attr is to support chrome bug - see #46
			   	// yyjob: check whether name is needed 
				   $('<input type="'+(o.multiple ? 'checkbox' : 'radio')+'" '+(this.selected ? 'checked="checked"' : '')+ (setName ? ' name="multiselect_'+id + '"' : '') + '/>')
					   .prop({checked:this.selected, disabled:isDisabled, 'aria-disabled':isDisabled, 'aria-selected':this.selected})
                  .attr({ id:inputID, title:title })
					   .val( value )
					   .appendTo( label )
					   .after('<span>'+title+'</span>');
            }
            else {
               li = $('<td />')
                  .addClass("ui-corner-all")
					   .addClass(isDisabled ? 'ui-multiselect-disabled' : '')
                  .addClass(requestUrl ? 'ui-multiselect-ajax' : '')
                  .attr("value", value)
                  .attr("title", title)
                  .addClass(allselectlist[value] ? "allsel" : (valueslist[value] ? "somesel" : ''))
                  .attr("values", valueslist[value] ? valueslist[value].values : '')
                  .attr("texts", valueslist[value] ? valueslist[value].texts : '')
					   .appendTo(colEl); 

				   label = $('<label>' + title + '</label>')
					   .attr('for', inputID)
					   .addClass(labelClasses.join(' '))
					   .appendTo( li );
               
            }
			}
		});
		
		// cache some moar useful elements
		this.labels = menu.find('label');
	

		// set widths
		this._setButtonWidth();
		this._setMenuWidth();
		this._setMenuHeight(); 

		// remember default value
		if(this.options.requestUrl) {
		   button[0].defaultValue = this.update2(true);
      }
      else {
		   button[0].defaultValue = this.update();
      }
		
		// broadcast refresh event; useful for widgets
		if( !init ){
			this._trigger('refresh');
		}

	},

	// binds events
	_bindEvents: function(){
		var self = this, button = this.button;
		
		function clickHandler(){
			self[ self._isOpen ? 'close' : 'open' ]();
			return false;
		}
		
		// webkit doesn't like it when you click on the span :(
		button
			.find('span')
			.bind('click.multiselect', clickHandler);
		
		// button events
		button.bind({
			click: clickHandler,
			keypress: function(e){
				switch(e.which){
					case 27: // esc
					case 38: // up
					case 37: // left
						self.close();
						break;
					case 39: // right
					case 40: // down
						self.open();
						break;
				}
			},
			mouseenter: function(){
				if( !button.hasClass('ui-state-disabled') ){
					$(this).addClass('ui-state-hover');
				}
			},
			mouseleave: function(){
				$(this).removeClass('ui-state-hover');
			},
			focus: function(){
				if( !button.hasClass('ui-state-disabled') ){
					$(this).addClass('ui-state-focus');
				}
			},
			blur: function(){
				$(this).removeClass('ui-state-focus');
			}
		});

		// header links
		this.header
			.delegate('a', 'click.multiselect', function(e){
				// close link
				if( $(this).hasClass('ui-multiselect-close') ){
					self.close();
			
				// check all / uncheck all
				} else {
					self[ $(this).hasClass('ui-multiselect-all') ? 'checkAll' : 'uncheckAll' ]();
				}
			
				e.preventDefault();
			});
		
		// optgroup label toggle support
		this.menu
			.delegate('li.ui-multiselect-optgroup-label a', 'click.multiselect', function(e){
				e.preventDefault();
				
				var $this = $(this),
					$inputs = $this.parent().nextUntil('li.ui-multiselect-optgroup-label').find('input:visible:not(:disabled)');
				
				// trigger event and bail if the return is false
				if( self._trigger('optgrouptoggle', e, { inputs:$inputs.get(), label:$this.parent().text(), checked:$inputs[0].checked }) === false ){
					return;
				}
				
				// toggle inputs
				self._toggleChecked(
					$inputs.filter(':checked').length !== $inputs.length,
					$inputs
				);
			})
			.delegate('label', 'mouseenter.multiselect', function(){
				if( !$(this).hasClass('ui-state-disabled') ){
					self.labels.removeClass('ui-state-hover');
					$(this).addClass('ui-state-hover').find('input').focus();
				}
			})
			.delegate('label', 'keydown.multiselect', function(e){
				switch(e.which){
					case 9: // tab
					case 27: // esc
						self.close();
						break;
					case 38: // up
					case 40: // down
					case 37: // left
					case 39: // right
						self._traverse(e.which, this);
						e.preventDefault();
						break;
					case 13: // enter
						e.preventDefault();
						$(this).find('input')[0].click();
						break;
				}
			})
			.delegate('input[type="checkbox"], input[type="radio"]', 'click.multiselect', function(e){
				var $this = $(this),
					val = this.value,
					checked = this.checked,
					tags = self.element.find('option');
				
				// bail if this input is disabled or the event is cancelled
				if( $this.is(':disabled') || self._trigger('click', e, { value:val, text:this.title, checked:checked }) === false ){
					e.preventDefault();
					return;
				}
				
				// toggle aria state
				$this.prop('aria-selected', checked);
				
				// set the original option tag to selected
				tags.filter(function(){
					return this.value === val;
				}).prop('selected', (checked ? true : false));
				
				// make sure the original option tags are unselected first 
				// in a single select
				if( !self.options.multiple ){
					tags.not(function(){
						return this.value === val;
					}).prop('selected', false);
					
					self.labels.removeClass('ui-state-active');
					$this.closest('label').toggleClass('ui-state-active', checked );
					
					// close menu
					self.close();
				}
				
				// setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827
				// http://bugs.jquery.com/ticket/3827 
				setTimeout($.proxy(self.update, self), 10);
			})
         .delegate("a.ui-multiselect-showdata", 'click.multiselect', function(e){
				var $this = $(this),
                datacontainer = $("#job_multiflatselect_datacontainer", self.menu);

            if(datacontainer) {
               if(datacontainer.multiflatselect("isOpen"))
                  datacontainer.multiflatselect("close");
               else 
                  datacontainer.multiflatselect("open");
            }
         }); 
		
		// close each widget when clicking on any other element/anywhere else on the page
		// yyjob: disable mousedown.multiselect event if we have requestUrl for datasection 
		if(!self.options.requestUrl && !self.options.dataSection) {
		   $(document).bind('mousedown.multiselect', function(e){
			   if(self._isOpen && !$.contains(self.menu[0], e.target) && e.target !== self.button[0]){
				   self.close();
			   }
		   });
		}
      else if(!self.options.dataSection) {
         // only exist if click out of menu area. 
		   $(document).bind('mousedown.multiselect', function(e){
			   if(self._isOpen && !$.contains(self.menu[0], e.target) && e.target !== self.button[0]){
				   self.close();
			   }
		   });
      }

		// deal with form resets.  the problem here is that buttons aren't
		// restored to their defaultValue prop on form reset, and the reset
		// handler fires before the form is actually reset.  delaying it a bit
		// gives the form inputs time to clear.
		$(this.element[0].form).bind('reset.multiselect', function(){
			setTimeout(function(){ self.update(); }, 10);
		});
	},

	// binds table events
	_bindTableEvents: function(){
		var self = this, button = this.button;

		// unbind non-data section 
		if(!this.options.dataSection && this.options.requestUrl != null)
		   this.menu.undelegate("label", "mouseenter.multiselect"); 

		this.menu
			.delegate('td.ui-multiselect-ajax', 'click.multiselect', function(e){
				var $this = $(this),
					val = $this.attr("value"),
					text = $this.find('label').text(),
               values = $this.attr("values").split(","); 

            if($this.hasClass("somesel") || $this.hasClass("allsel")) {
               $this.removeClass("allsel").removeClass("somesel"); 
               $this.removeClass("ui-state-hover").removeClass("ui-multiselect-hover");
               $this.attr("values", '').attr("texts", ''); 
               // don't need to call uncheckAll as the data container might not be ready, there is no checkbox for deselection 
				   // setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827
            	// http://bugs.jquery.com/ticket/3827 
				   setTimeout($.proxy(self.update2, self), 10);

               var datacontainer = $("#job_multiflatselect_datacontainer", self.menu);
               if(datacontainer)
                  datacontainer.multiflatselect("close"); 

               return;
            }

		      self.menu.find('td.ui-multiselect-ajax').removeClass('ui-state-hover').removeClass("ui-multiselect-hover"); 
            $this.addClass("ui-state-hover").addClass("ui-multiselect-hover");

				var data = $.extend({}, self.options.requestData, {"value": val, "text": text}); 
            $.ajax({
               url: self.options.requestUrl, 
               data: data, 
               dataType: self.options.requestDataType, 
               success:function(data) {
                  var datacontainer = $("#job_multiflatselect_datacontainer", self.menu);
                  if(datacontainer.length <= 0) 
                     datacontainer = $("<select " + (self.options.dataMultiple ? "multiple='multiple'" : "") + " id='job_multiflatselect_datacontainer'></select>").appendTo(self.menu); 
                  else {
                     datacontainer.multiflatselect("destroy");
                  }

                  /* for newly created list, need to display as selected input list */
                  datacontainer.empty().addOption(data, false).selectOptions(values).multiflatselect({
                     header: self.options.dataHeader,
                     noneSelectedText: "",
                     checkAllText: self.options.dataCheckAllText, 
                     uncheckAllText: self.options.dataUncheckAllText,
                     showDataSectionText: self.options.showDataSectionText, 
                     minWidth: self.options.minWidth,
                     width: "100%", 
                     name: false,
                     height: "auto",  
                     listStyle: true, 
                     colNum: self.options.dataColNum, 
                     autoOpen: false,
                     dataContainerHeight: 175, 
                     dataSection: true, 
                     multiple: self.options.dataMultiple, 
                     selectedText: function(numChecked, numTotal, checkedItems){
                        if(numChecked == numTotal) {
                           $this.addClass("allsel").removeClass("somesel"); 
                        } else if(numChecked == 0) {
                           $this.removeClass("allsel").removeClass("somesel"); 
                        } else {
                           $this.addClass("somesel").removeClass("allsel"); 
                        }

                        var s = jQuery(checkedItems).map(function(){ return this.title; }).get().join(', '); 
                        var v = jQuery(checkedItems).map(function(){ return this.value; }).get().join(','); 
                        $this.attr("values", v).attr("texts", s); 

				            // setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827
            				// http://bugs.jquery.com/ticket/3827 
				            setTimeout($.proxy(self.update2, self), 10);

                        return ''; 
                  
                     }
                  }).bind("multiflatselectclick multiflatselectcheckall multiflatselectuncheckall", function( event, ui ){
                        /* we can do samething in selectedText callback 
                        var values = $.map($(this).multiflatselect("getChecked"), function( input ){
                           return input.value; 
                        }).join(",");
                        var texts = $.map($(this).multiflatselect("getChecked"), function( input ){
                           return input.title; 
                        }).join(",");

                        */
                  })

               }
            }); 
			})
			.delegate('td.ui-multiselect-ajax', 'mouseenter.multiselect', function(){
				if( !$(this).hasClass('ui-state-disabled') ){
		         self.menu.find('td.ui-multiselect-ajax').not(".ui-multiselect-hover").removeClass('hover');
					$(this).addClass('hover'); //.find('td').focus();
				}
			});
		
	},
	// open the menu
	open: function(e){
		var self = this,
			button = this.button,
			menu = this.menu,
			speed = this.speed,
			o = this.options;
		
		// bail if the multiselectopen event returns false, this widget is disabled, or is already open 
		if( this._trigger('beforeopen') === false || button.hasClass('ui-state-disabled') || this._isOpen ){
			return;
		}
		
      //yyjob: container is menu now 
		var $container = menu.find('table:first'),
			effect = o.show,
			pos = button.position();
		
		// figure out opening effects/speeds
		if( $.isArray(o.show) ){
			effect = o.show[0];
			speed = o.show[1] || self.speed;
		}
		
		// set the scroll of the checkbox container
		menu.scrollTop(0).height(o.dataSection ? o.height : "auto");
		
		// position and show menu
		if( $.ui.position && !$.isEmptyObject(o.position) ){
			o.position.of = o.position.of || button;
			
			menu
				.show()
				.position( o.position )
				.hide()
				.show( effect, speed );
		
		// if position utility is not available...
		} else if(!o.dataSection) {
			menu.css({ 
				top: pos.top+button.outerHeight(),
				left: pos.left
			}).show( effect, speed );
		} else {
         menu.css({
            top: 0,
            left: 0, 
            position: "relative"
         }).show( effect, speed );
      }
		
		// select the first option
		// triggering both mouseover and mouseover because 1.4.2+ has a bug where triggering mouseover
		// will actually trigger mouseenter.  the mouseenter trigger is there for when it's eventually fixed
		this.labels.eq(0).trigger('mouseover').trigger('mouseenter').find('input').trigger('focus');
		
		button.addClass('ui-state-active');
		this._isOpen = true;
		this._trigger('open');
	},

	// updates the button text.  call refresh() to rebuild
	update: function(){
		var o = this.options,
			$inputs = this.labels.find('input'),
			$checked = $inputs.filter(':checked'),
			numChecked = $checked.length,
			value;

       //yyjob: honor the callback in any case 
   	if($.isFunction(o.selectedText)){
			value = o.selectedText.call(this, numChecked, $inputs.length, $checked.get());
	   } else if( numChecked === 0 ){
		   value = o.noneSelectedText;
		} else if( /\d/.test(o.selectedList) && o.selectedList > 0 && numChecked <= o.selectedList){
			value = $checked.map(function(){ return this.title; }).get().join(', ');
		} else {
			value = o.selectedText.replace('#', numChecked).replace('#', $inputs.length);
		}

		this.buttonlabel.html( value );
		return value;
	},

   // updates root label if any data section input is clicked 
   update2: function(trigger) {
		var o = this.options,
          $tdall = this.menu.find("td.allsel"),
          $tdsome = this.menu.find("td.somesel"),
          values=[], labelvalue='', allvalues=[];

      if($tdall.length == 0 && $tdsome.length ==0) {
          labelvalue = o.noneSelectedText;
      } else {
         if($tdall.length > 0) {
            var s = $tdall.map(function(){ return this.title; }).get(); 
            labelvalue += s.join(', '); 

            var v = $tdall.map(function(){ 
                  var vv = {}; 
                  vv[$(this).attr("value")]= $(this).attr("values");
                  return vv;
            }).get(); 

            $.merge(values, v);
            v = $tdall.map(function(){ return $(this).attr("value"); }).get().join(',');
            values[values.length++] = {'allselect': v };  
         }
         if($tdsome.length >0) {
            var s  = $tdsome.map(function(){ return this.title + "(" + $(this).attr("texts") + ")"; }).get(); 
            if(labelvalue.length > 0) labelvalue += ", "; 
            labelvalue += s.join(', '); 

            var v = $tdsome.map(function(){
                  var vv = {}; 
                  var vvv = {};
                  vvv[$(this).attr("values")] = $(this).attr("texts"); 
                  vv[$(this).attr("value")]= vvv; 
                  return vv;
            }).get();
            $.merge(values, v);
         }
      }

      var valueslist = flatMultiSelectData(values); 
      valueslist.label = values.length > 0 ? labelvalue : ''; 
      if(trigger === true) {
         var self =  this; 
		   setTimeout(function() {
               self._trigger("store", null, JSON.stringify(valueslist));
         }, 1000);
      }
      else 
         this._trigger("store", null, JSON.stringify(valueslist));

      this.buttonlabel.html(labelvalue); 
      return labelvalue; 
   },

	// react to option changes after initialization
	_setOption: function( key, value ){
		var menu = this.menu;
		
		switch(key){
			case 'header':
				menu.find('div.ui-multiselect-header')[ value ? 'show' : 'hide' ]();
				break;
			case 'checkAllText':
				menu.find('a.ui-multiselect-all span').eq(-1).text(value);
				break;
			case 'uncheckAllText':
				menu.find('a.ui-multiselect-none span').eq(-1).text(value);
				break;
			case 'height':
				menu.find('table:first').height( parseInt(value,10) );
				break;
			case 'minWidth':
				this.options[ key ] = parseInt(value,10);
				this._setButtonWidth();
				this._setMenuWidth();
				break;
			case 'selectedText':
			case 'selectedList':
			case 'noneSelectedText':
				this.options[key] = value; // these all needs to update immediately for the update() call
				this.update();
				break;
			case 'classes':
				menu.add(this.button).removeClass(this.options.classes).addClass(value);
				break;
		}
		
		$.Widget.prototype._setOption.apply( this, arguments );
	}, 

	// set button width
	_setButtonWidth: function(){
		var width = this.element.outerWidth(),
			o = this.options,
		   m = this.menu;
			
		if( /\d/.test(o.minWidth) && width < o.minWidth){
			width = o.minWidth;
		}
		
      var v = 	parseInt(m.css('padding-left'),10) + 
				parseInt(m.css('padding-right'),10) +
				parseInt(m.css('border-right-width'),10) +
				parseInt(m.css('border-left-width'),10);

		// set widths
		this.button.css("min-width", width );
		this.button.css("max-width", this.options.dataSection ? width : (this.options.menuWidth ? (this.options.menuWidth+v) : width)); 
		this.button.css("width", width );
	},

	// set menu width
	_setMenuWidth: function(){
		var m = this.menu,
			width = this.button.outerWidth()-
				parseInt(m.css('padding-left'),10)-
				parseInt(m.css('padding-right'),10)-
				parseInt(m.css('border-right-width'),10)-
				parseInt(m.css('border-left-width'),10);
				
		m.width( this.options.dataSection ? "100%" : (this.options.menuWidth ? this.options.menuWidth : (width || this.button.outerWidth() )));
	},

   // set menu height 
   _setMenuHeight: function() {
      var m = this.menu; 

      if(!this.options.dataSection) {
         // get data section height 
      }
      m.height("auto"); 
   }
      
   });
})(jQuery);


